<template>
    <div>
        <el-container>
            <el-main>
                <h1>投票项目</h1>
                <h2>投票名称：<strong>{{ name }}</strong></h2>
                <h3 style="text-align: left;"><strong>发起人：</strong>{{ promoter }}</h3>
                <h3 style="text-align: left;">投票状态：{{ (status == 0) ? '立项' : ((status == 1) ? '正在投票' : '投票截止') }}</h3>
                <p style="text-align: left;"><b>项目描述：</b>{{ projectDescription }}</p>
                <v-echart :option="chartData" class="chart" autoresize/>
                <el-row :gutter="20" class="el-row" type="flex">
                    <el-col :span="8" v-for="(item, index) in candidateList" :key="item.id" class="el-col" style="margin-bottom: 20px;">
                        <el-card :key="item.id" class="el-card" shadow="hover">
                            <div slot="header" class="clearfix">
                                <span>{{ item.candname }}</span>
                                <div style="float: left; padding: 0 3px">{{ voteNum[index] }} 票</div>
                                <el-button v-if="status==1" style="float: right; padding: 3px 0" type="text" @click="voteForCandidate(item.id)">投票</el-button>
                            </div>
                        <div class="text item">{{ item.description }}</div>
                        </el-card>
                    </el-col>
                </el-row>
            </el-main>
            <el-dialog
                title="算法展示"
                :visible.sync="algoDialogVisible"
                width="60%"
                :close-on-click-modal="false"
                :before-close="handleClose">
                <span>{{ dialogContent }}</span>
                <span slot="footer" class="dialog-footer">
                    <el-button type="primary" @click="algoDialogVisible = false">关 闭</el-button>
                </span>
            </el-dialog>
        </el-container>
    </div>    
</template>
<script>
const bigInt = require('big-integer');
import Voter from "../algo.js"
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers';
import { BarChart } from 'echarts/charts'
import { GridComponent } from 'echarts/components';
use([CanvasRenderer, BarChart, GridComponent]);
export default {
    data() {
        return {
            candidateList: [],
            name: '',
            promoter: '',
            status: 0,
            projectId: 0,
            projectDescription: '',
            voteNum: [],
            algoDialogVisible: false,
            dialogContent: '',
            chartData: {
                xAxis: {
                    type: 'category',
                    data: [],
                },
                yAxis: {
                    type: 'value',
                },
                series: {
                    type: 'bar',
                    data: [],
                    label: {
                        show: true,
                        position: 'top',
                    }
                }
            }
        }
    },
    methods: {
        async runVoteAlgo(candidateId) {
            this.dialogContent = ''
            const _this = this;
            console.log('Vote: candidateId ', candidateId);
            let tickets = new Array();
            for (let i = 0; i < this.candidateList.length; ++i) {
                tickets.push(this.candidateList[i].id);
            }
            try {
                this.algoDialogVisible = true;
                if (sessionStorage.getItem('userid') == null) {
                    console.log("userid is null")
                    this.dialogContent += '用户未登录'
                    throw('用户未登录！');
                }
                let messageSets = null;
                let publicModulus = null;
                let publicExponent = null;
                let voter = null;
                this.dialogContent += "从服务器获取公钥信息："
                await this.$axios.post('/user/getpubkey', null).then((res) => {
                    if (res.data['code'] != 1) {
                        throw(res.data['msg']);
                    }
                    const serverPubKeyString = res.data['data']; // 用 / 分隔
                    publicModulus = bigInt(serverPubKeyString.split('/')[0], 10);
                    this.dialogContent += ("模：" + publicModulus.toString());
                    publicExponent = bigInt(serverPubKeyString.split('/')[1], 10);
                    this.dialogContent += ("，指数：" + publicExponent.toString());
                    console.log('Public Modulus:', publicModulus.toString());
                    console.log('Public Exponent:', publicExponent.toString());
                    voter = new Voter(parseInt(sessionStorage.getItem('userid')), tickets);
                    messageSets = voter.generateBlindMessageSets(publicModulus, publicExponent);
                    for (let i = 0; i < 10; ++i) {
                        this.dialogContent += ("\nBlindSignatureRequest["+i+"]={uid="+messageSets[i].uid+", 盲化后消息="+ messageSets[i].blindMessage+"}");
                    }
                }).catch(function (error) {
                    console.error(error);
                    _this.$notify.error({
                        title: '获取公钥失败',
                        message: '错误：' + error
                    });
                    throw(error);
                });
                this.dialogContent += "\n向服务器发送以上盲签名请求。"
                console.log("POST to /user/getmessage ", messageSets);
                let chosenMessageSet = 0;
                await this.$axios.post('/user/getmessage', {
                    messageSets: messageSets,
                    proid: parseInt(this.projectId),
                }).then((res) => {
                    if (res.data['code'] != 1) {
                        throw(res.data['msg']);
                    }
                    chosenMessageSet = res.data['data'];
                    this.dialogContent += ("\n服务器选中编号：" + chosenMessageSet);
                }).catch(function (error) {
                    console.error(error);
                    _this.$notify.error({
                        title: '提交盲签名请求失败',
                        message: '错误：' + error
                    });
                    throw(error);
                });
                // 发送盲因子
                let blindingFactorsToBeSent = new Array(10);
                console.log("chosenMessageSet is ", chosenMessageSet)
                this.dialogContent += "\n将其余组盲因子发送给服务器";
                for (var k = 0; k < 10; ++k) {
                    if (k == chosenMessageSet) {
                        blindingFactorsToBeSent[chosenMessageSet] = null;
                    } else {
                        blindingFactorsToBeSent[k] = voter.blindingFactors[k].toString();
                    }
                }
                let signedMessages = null;
                console.log('POST to /user/signmessage, blindingFactors: blindingFactorsToBeSent,',  blindingFactorsToBeSent)
                await this.$axios.post('/user/signmessage', {
                    uid: parseInt(sessionStorage.getItem('userid')),
                    proid: parseInt(this.projectId),
                    blindingFactors: blindingFactorsToBeSent,
                }).then((res) => {
                    if (res.data['code'] != 1) {
                        throw(res.data['msg']);
                    }
                    signedMessages = res.data['data']['blindMessage']; // String[]
                    this.dialogContent += ("\n服务器发回盲签名后的消息集：{");
                    for (let i = 0; i < signedMessages.length; ++i) {
                        this.dialogContent += (signedMessages[i] + ",");
                    }
                    this.dialogContent += "}";
                }).catch(function (error) {
                    console.error(error);
                    _this.$notify.error({
                        title: '提交盲因子失败',
                        message: '错误：' + error
                    });
                    throw(error);
                });
                // 投票
                let unblindSignedMessageSet = voter.unblindOneMessageSet(signedMessages, /*((RSAPublicKey) keyPair.getPublic()).getModulus()*/publicModulus, /*((RSAPublicKey) keyPair.getPublic()).getPublicExponent()*/publicExponent, chosenMessageSet);
                this.dialogContent += ("\n对服务器发回的盲签名消息集去盲得到：{");
                for (let i = 0; i < unblindSignedMessageSet.length; ++i) {
                    this.dialogContent += (bigInt(unblindSignedMessageSet[i],10).toString() + ",");
                }
                this.dialogContent += "}";
                let i = 0;
                for (i = 0; i < this.candidateList.length; ++i) {
                    if (this.candidateList[i].id == candidateId) break;
                }
                let voteMessage = unblindSignedMessageSet[i /* 相对index */];
                this.dialogContent += ("\n投出 " + bigInt(voteMessage,10).toString());
                await this.$axios.post('/user/vote', {
                    proid: parseInt(this.projectId),
                    signedmessage: voteMessage,
                }).then((res) => {
                    if (res.data['code'] != 1) {
                        throw(res.data['msg']);
                    }
                    this.dialogContent += ("\n投票成功！");
                }).catch(function (error) {
                    console.error(error);
                    _this.$notify.error({
                        title: '提交投票失败',
                        message: '错误：' + error
                    });
                    throw(error);
                });
            } catch (error) {
                console.log(error);
                return -1;
            }
            return 0;
        },
        updateAllVoteNum() {
            for (let i = 0; i < this.candidateList.length; ++i) {
                this.$axios.post('/user/getvotenum', {
                    candid: this.candidateList[i].id,
                    proid: this.$route.query.projectid,
                }).then((res) => {
                    if (res.data['code'] != 1) {
                        throw(res.data['msg'])
                    }
                    this.$set(this.voteNum, i, res.data['data']['votenum']); // 这样更新数组才能重新响应渲染
                    this.$set(this.chartData.series.data, i, parseInt(res.data['data']['votenum']));
                }).catch(function (error) {
                    console.error(error);
                    _this.$notify.error({
                        title: '无法获取选票信息',
                        message: '具体错误 ' + error
                    });
                })
            }
        },
        async getProjectDetails() {
            console.log("Opening projectid ", this.$route.query.projectid);
            const _this = this
            await this.$axios.post('/user/getpro', {
                id: this.$route.query.projectid
            }).then((res) => {
                if (res.data['code'] != 1) {
                    throw(res.data['msg']);
                }
                this.name = res.data['data']['projectname'];
                this.projectDescription = res.data['data']['description'];
                this.promoter = res.data['data']['promoter'];
                this.projectId = res.data['data']['id'];
                // 0 立项 1 正在投票 2 投票截止
                this.status = res.data['data']['state'];
            }).catch(function (error) {
                console.error(error);
                _this.$notify.error({
                    title: '无法获取项目信息',
                    message: '具体错误 ' + error
                });
            });
            await this.$axios.post('/user/getcand', {
                id: this.$route.query.projectid,
            }).then((res) => {
                if (res.data['code'] != 1) {
                    throw(res.data['msg'])
                }
                this.voteNum = new Array(res.data['data'].length);
                this.candidateList = res.data['data'];
                this.chartData.series.data = new Array(res.data['data'].length);
                this.chartData.xAxis.data = new Array(res.data['data'].length);
                for (let i = 0; i < res.data['data'].length; ++i) {
                    this.chartData.xAxis.data[i] = this.candidateList[i].candname;
                }
            }).catch(function (error) {
                console.error(error);
                _this.$notify.error({
                    title: '无法获取候选人信息',
                    message: '具体错误 ' + error
                });
            });
            this.updateAllVoteNum();
        },
        async voteForCandidate(candidateId) {
            let isVoteSucceed = (await this.runVoteAlgo(candidateId) == 0);
            const h = this.$createElement;
            this.$msgbox({
                title: '消息',
                message: isVoteSucceed ? h('div', {class: 'wrap'}, [
                    h('i', {style: 'font-size: 50px; color: green', class: 'el-icon-success',}, null),
                    h('span', null, '投票成功'),
                ])
                :
                h('div', {class: 'wrap'}, [
                    h('i', {style: 'font-size: 50px; color: red', class: 'el-icon-error',}, null),
                    h('span', null, '投票失败'),
                ]),
                showCancelButton: false,
                confirmButtonText: '确定',
                beforeClose: (action, instance, done) => {
                    if (action === 'confirm') {
                        done();
                    }
                }
            }).then(action => {});
            this.updateAllVoteNum();
        },
        handleClose(done) {
            this.dialogContent = '';
            done();
        }
    },
    mounted() {
        this.getProjectDetails();
    }
}
</script>
<style>
  .text {
    font-size: 14px;
    text-align: left;
  }

  .item {
    margin-bottom: 18px;
  }

  .clearfix:before,
  .clearfix:after {
    display: table;
    content: "";
  }
  .clearfix:after {
    clear: both
  }

  .el-row {
    margin-bottom: 20px;
    display: flex;
    flex-wrap: wrap
  }
  .wrap {
    /* 可无需设置高度，靠图片高度 或者 文字的上下padding撑开高度 */
    /* width: 600px; */
    text-align: bottom;
  }
  .wrap span{
    display: inline-block;
    vertical-align: middle;
  }
  .wrap i{
    vertical-align: middle;
  }
  .el-dialog__body {
    white-space: pre-wrap;
    text-align: start;
  }
</style>
<style scoped>
.chart {
  height: 60vh;
}
</style>
